package com.edu.hre.common.utils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts.util.LabelValueBean;

import com.edu.hre.common.cache.PassiveDynaCache;
import com.edu.hre.common.entity.HrePageInfo;
import com.edu.hre.db.pojo.HreEmployee;
import com.edu.hre.db.pojo.HreSubunit;
import com.edu.hre.db.pojo.HreUser;
import com.edu.hre.db.service.BaseService;
import com.edu.hre.db.utils.Compare;
import com.edu.hre.struts.form.pim.EmployeeForm;

// Class chứa các hàm xử lý logic chung, các hàm public static ....
public class CommonUtils {

	private static final Logger logger = Logger.getLogger(CommonUtils.class);
	private static final BaseService baseService = BeanFinder.getBean(BaseService.BEAN_NAME, BaseService.class);
	private static final PassiveDynaCache passiveDynaCache = BeanFinder.getBean(PassiveDynaCache.BEAN_NAME, PassiveDynaCache.class);

	public static void println(String s) {
		s = "Printed from: " + Thread.currentThread().getStackTrace()[2].toString() + "\n" + s + "\n"; 
		System.out.println(s);
		logger.error(s);
	}

	public static String getStackTraceException(Exception e) {
		String all = "Stack trace of exception: ";
		try {
			all = e.getMessage() + "\n";
			StackTraceElement stackTrace[] = e.getStackTrace();
			int count = stackTrace.length;
			for (int i = count - 1; i >= 0; i--) {
				all += stackTrace[i].toString() + "\n";
			}
		} catch (Exception e2) {
			all += e2.getMessage();
		}
		return all;
	}

	public static int parseInt(String value, int defauzt) {
		try {
			return Integer.parseInt(value);
		} catch (Exception e) {
			return defauzt;
		}
	}

	public static int getTotalPage(int totalItem) {

		int pageSize = CommonUtils.parseInt(
				DynamicConfig.getConfig(DynamicConfig.PAGE_SIZE), 20);
		double amount = Math.ceil((double) totalItem / pageSize);
		return (int) amount;
	}
	
	public static String to2DigitalInt (int input) {
		if (input < 0 || input >= 10) {
			return "" + input;
		}

		return "0" + input;
	}

	public static ArrayList<HrePageInfo> getDisplayingPages(int current,
			int totalPages) {

		ArrayList<HrePageInfo> allPages = new ArrayList<HrePageInfo>();
		if (totalPages == 1) {
			return allPages;
		}

		if (totalPages <= 5) {
			for (int i = 1; i <= totalPages; i++) {
				allPages.add(new HrePageInfo("" + i, "" + i));
			}
		} else {
			int start = 0;
			int end = 0;
			if (current == 1 || current == 2) {
				start = 1;
				end = 5;
			} else if (current == totalPages - 1 || current == totalPages) {
				start = totalPages - 4;
				end = totalPages;
			} else {
				start = current - 2;
				end = current + 2;
			}

			if (start != 1) {
				allPages.add(new HrePageInfo("First", "1"));
				allPages.add(new HrePageInfo("Prev", "" + (current - 1)));
			}

			for (int i = start; i <= end; i++) {
				allPages.add(new HrePageInfo("" + i, "" + i));
			}

			if (end != totalPages) {
				allPages.add(new HrePageInfo("Next", "" + (current + 1)));
				allPages.add(new HrePageInfo("Last", "" + totalPages));
			}
		}

		for (int i = 0; i < allPages.size(); i++) {
			HrePageInfo page = allPages.get(i);
			if (page.getValue().equals("" + current)
					&& Character.isDigit(page.getLabel().toCharArray()[0])) {
				page.setIsCurrent("true");
				break;
			}
		}

		return allPages;
	}

	public static String getSourceCodeLocation() {
		return "Info of location: "
				+ Thread.currentThread().getStackTrace()[2].toString();
	}

	public static String getLineNumber() {
		return "Line of code: "
				+ Thread.currentThread().getStackTrace()[2].getLineNumber();
	}

	public static String getStackTrace() {
		String all = "StackTrace of code: ";
		try {
			StackTraceElement stackTrace[] = Thread.currentThread()
					.getStackTrace();
			int count = stackTrace.length;
			for (int i = count - 1; i >= 0; i--) {
				all += stackTrace[i].toString() + "\n";
			}
		} catch (Exception e2) {
		}
		return all;
	}

	public static void sortCollection(List list, boolean isAsc, String sortBy) {

		if (list == null || list.size() <= 1) {
			return;
		}
		int size = list.size();
		for (int i = 0; i < size - 1; i++) {
			for (int j = i + 1; j < size; j++) {
				Object objI = list.get(i);
				Object objJ = list.get(j);
				if (objI != null && compare(objI, sortBy, objJ, isAsc) > 0) {
					Object temp = objI;
					list.set(i, objJ);
					list.set(j, temp);
				}
			}
		}
	}

	public static boolean isDigitString(String s) {
		try {
			Integer.parseInt(s);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public static int compare(Object o1, String sortBy, Object o2, boolean isAsc) {
		try {
			String getter = "get"
					+ String.valueOf(sortBy.charAt(0)).toUpperCase()
					+ sortBy.substring(1);
			Method method = o1.getClass().getMethod(getter);
			Object result1 = method.invoke(o1);
			Object result2 = method.invoke(o2);
			if (result1 == null && result2 == null) {
				return 0;
			}
			Integer finalResult = 0;
			if (result1 == null) {
				finalResult = -1;
			}
			else if (result2 == null) {
				finalResult = 1;
			}
			else if (result1.getClass() != result2.getClass()) {
				throw new Exception();
			}  else {
				if(result1 instanceof java.sql.Date){
					java.sql.Date d1 = (java.sql.Date) result1;
					java.sql.Date d2 = (java.sql.Date) result2;
					finalResult = d1.compareTo(d2);
				} else {
					Method compareToMethod = result1.getClass().getMethod("compareTo", result2.getClass());
					finalResult = (Integer) compareToMethod.invoke(result1, result2);
				}
			}
			if(isAsc){
				return -finalResult;
			}
			return finalResult;
		} catch (Exception e) {
			println("Compare exception with column = " + sortBy + "\n" + e.getMessage());
		}
		return 0;
	}

	public static String dateToString (Date date) {

		try {
			
			// yyyy-mm-dd
			String result = "";
			
			int month = date.getMonth() + 1;
			if (month >= 10) {
				result += month;
			} else {
				result += "0" + month;
			}
			result += "/";

			int day = date.getDate();
			if (day >= 10) {
				result += day;
			} else {
				result += "0" + day;
			}
			result += "/";
			
			result += date.getYear();
			
			return result;
		} catch (Exception e) {
			return "";
		}
	}
	
	public static Date stringToDate (String str) {
		
		try {
			String[] elements = str.split("/");
			Date date = new Date();
			date.setYear(Integer.parseInt(elements[2]));
			date.setMonth(Integer.parseInt(elements[0]) - 1);
			date.setDate(Integer.parseInt(elements[1]));
			return date;
		} catch (Exception e) {
			return null;
		}
	}
	
	public static Calendar stringToCalendar (String str) {
		
		try {
			String[] elements = str.split("/");
			Calendar date = Calendar.getInstance();
			date.set(Integer.parseInt(elements[2]), Integer.parseInt(elements[0]) - 1, Integer.parseInt(elements[1]));
			return date;
		} catch (Exception e) {
			return null;
		}
	}
	
	public static boolean checkLaterThanToday (String date, int hour, int minute, int second) {
		
		Calendar now = Calendar.getInstance();
		Calendar cal = stringToCalendar(date);
		if (cal == null) {
			return false;
		}
		
		if (hour >=0) {
			cal.set(Calendar.HOUR_OF_DAY, hour);
		}
		if (minute >=0) {
			cal.set(Calendar.MINUTE, minute);
		}
		if (second >=0) {
			cal.set(Calendar.SECOND, second);
		}

		return cal.after(now);
	}
	
	public static String verifyUserName (String userName, boolean isVerifyingExistence) {
		
		// Verify the first character.
		char first = userName.charAt(0);
		boolean firstOk = (first >= 'a' && first <= 'z') || (first >= 'A' || first <= 'Z');
		if (!firstOk) {
			return "The first character of user name must be [a-zA-Z]";
		}
		
		// Check invalid characters
		for (int ind = 0; ind < userName.length(); ind++) {
			char tmp = userName.charAt(ind);
			boolean ok = (tmp >= 'a' && tmp <= 'z') || (tmp >= 'A' && tmp <= 'Z')
					|| (tmp >= '0' && tmp <= '9');
			
			if (!ok) {
				return "User name contains invalid characters! (not [a-zA-Z0-9])";
			}
		}
		
		if (isVerifyingExistence) {
			// Verify the uniqueness of the user name.
			HreUser tmp = baseService.getFirstPojo("userName", Compare.EQUAL, userName, null, null, 0, HreUser.class);
			if (tmp != null) {
				return "The user name already exists!";
			}
		}
		
		return "";
	}
	
	public static byte[] convertStringToByteArray(String target, String encoding) {
		try {
			return target.getBytes(encoding);
		} catch (UnsupportedEncodingException e) {
			return target.getBytes();
		}
	}  
	
	public static String verifyPassword (String password) {
		
		if (password.length() < 6) {
			return "Password must have at least 6 characters!";
		}
		
		return "";
	}
	
	public static boolean verifyEmail (String emailAddress) {
			emailAddress = emailAddress.toLowerCase();
		    return emailAddress.contains(" ") == false && emailAddress.matches(".+@.+\\.[a-z]+");
	}
	
	public static boolean verifyPhoneNumber (String phoneNumber) {
		
		if (phoneNumber == null) {
			return false;
		}

		// Check invalid characters
		for (int ind = 0; ind < phoneNumber.length(); ind++) {
			char tmp = phoneNumber.charAt(ind);
			boolean ok = (tmp == ' ') || (tmp == '-') || (tmp == '+')
					|| (tmp >= '0' && tmp <= '9');
			
			if (!ok) {
				return false;
			}
		}
		
		int loc = phoneNumber.indexOf('+');
		if (loc >= 1) {
			return false;
		}
		
		return true;
	}

	public static boolean verifyFaxNumber (String faxNumber) {

		return verifyPhoneNumber(faxNumber);
	}
	
	public static int getIntParameter (String paramName, HttpServletRequest request) {
		
		if (paramName == null) {
			return -1;
		}
		
		try {
			String paramVal = request.getParameter(paramName);
			int result = Integer.parseInt(paramVal);
			return result;
		} catch (Exception e) {
			return -1;
		}
	}
	
	public static String normalizeString (String input, String defaultStr) {
		
		try {
			return input.trim();
		} catch (Exception e) {
			return defaultStr;
		}
	}
	
	public static String normalizeString (String input) {
			
		return normalizeString(input, input);
	}
	
	public static String normalizeObjectToString (Object input, String defaultStr) {
		
		if (input == null || input.toString() == null) {
			return defaultStr;
		}

		try {
			return input.toString().trim();
		} catch (Exception e) {
			return defaultStr;
		}
	}
	
	public static double normalizeDouble (Double input, double defaultDou) {
		
		if (input == null) {
			return defaultDou;
		}

		return input.doubleValue();
	}
	
	public static double normalizeDouble (Double input) {
		
		return normalizeDouble(input, 0);
	}
	
	public static int normalizeInteger (Integer input, int defaultInt) {
		
		if (input == null) {
			return defaultInt;
		}

		return input.intValue();
	}
	
	public static int normalizeInteger (Integer input) {
		
		return normalizeInteger(input, 0);
	}
	
	public static short normalizeShort (Short input, short defaultInt) {
		
		if (input == null) {
			return defaultInt;
		}

		return input.shortValue();
	}
	
	public static short normalizeShort (Short input) {
		
		return normalizeShort(input, (short) 0);
	}
	
	public static byte normalizeByte (Byte input, byte defaultInt) {
		
		if (input == null) {
			return defaultInt;
		}

		return input.byteValue();
	}
	
	public static byte normalizeByte (Byte input) {
		
		return normalizeByte(input, (byte) 0);
	}
	
	public static String getFullName(HreEmployee employee){
		return employee.getEmpLastname() + (StringUtils.isBlank(employee.getEmpMiddleName())
				? " " : " " + employee.getEmpMiddleName() + " ") + employee.getEmpFirstname();
	}

	public static float parseFloat(String config, float f) {
		try {
			return Float.parseFloat(config);
		} catch (Exception e) {
			return f;
		}
	}
	
	public static Integer getEmpNumber (String nameAndUsername) {
		
		if (nameAndUsername == null) {
			return null;
		}
		
		String sql = "SELECT emp.emp_number AS col1" +
				" FROM hre_employee AS emp" +
				" WHERE CONCAT(emp.emp_lastname, ' ', emp.emp_middle_name, ' ', emp.emp_firstname, ' [', emp.main_username, ']') = ? ";
		
		List <Object[]> listObject = baseService.listArrayObject(sql, -1, 0, nameAndUsername);
		if (listObject == null || listObject.size() != 1) {
			return null;
		}

		try {
			Object[] objs = listObject.get(0);

			String empNumber = CommonUtils.normalizeObjectToString(objs[1], "0");
			return CommonUtils.parseInt(empNumber, -1);

		} catch (Exception e) {
			CommonUtils.println(e.getMessage());
		}

		return null;
	}
	
	
	public static String getEmpNameAndUsername(HreEmployee emp) {
		if (emp == null) {
			return "";
		}
		String tmp = getFullName(emp);
		if(StringUtils.isNotBlank(emp.getMainUsername())){
			tmp += " [" + emp.getMainUsername() + "]";
		}
		return tmp;
	}
	
	public static String getEmpNameAndUsername (Integer empNumber) {
		if (empNumber == null) {
			return "";
		}
		HreEmployee emp = baseService.getFirstPojo("empNumber", Compare.EQUAL, empNumber, HreEmployee.class);
		return getEmpNameAndUsername(emp);
	}
	
	public static List<LabelValueBean> getSubunits(){
		List<LabelValueBean> listTmp = (List<LabelValueBean>) passiveDynaCache.getCachedItem(RequestUtils.Attribute.LIST_SUB_UNITS_STRUCTURE);
		if(listTmp == null || listTmp.size() == 0){
			listTmp = new ArrayList<LabelValueBean>();
			List<HreSubunit> listSubunit = baseService.getPojos("level", Compare.EQUAL, new Short("1"), null, null, 0, 0, HreSubunit.class);
			tagSubunits(listSubunit, 1, listTmp);
		} 
		if(listTmp != null && listTmp.size() != 0){
			passiveDynaCache.setCachedItem(RequestUtils.Attribute.LIST_SUB_UNITS_STRUCTURE, PassiveDynaCache.TIMEOUT_3_HOURS, listTmp);
		}
		return listTmp;
	}
	
	
	private static void tagSubunits(List<HreSubunit> listSubunit, int level, List<LabelValueBean> listTmp){
		for(int i = 0; listSubunit != null && i < listSubunit.size(); i ++){
			HreSubunit tmp = listSubunit.get(i);
			listTmp.add(new LabelValueBean(getPrefix(level) + tmp.getName(), "" + tmp.getId()));
			List<HreSubunit> listChild = baseService.getPojos("parentId", Compare.EQUAL, tmp.getId(), null, null, 0, 0, HreSubunit.class);
			tagSubunits(listChild, level + 1, listTmp);
		}
	}
	
	private static String getPrefix(int level){
		String str = "";
		for(int i = 0; i < level - 1; i++){
			str += ". . ";
		}
		str += "|_";
		return str;
	}
}